﻿//Copyright (C) <2013>  <jonathan cleeve norton> All Rights Reserved 
//Contact jon.norton@fin-plus.co.uk website <http://www.fin-plus.co.uk/>
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Dynamic;
using System.Linq;
using System.Text;
using FinPlusInterfaces;

namespace FinPlusBaseCore
{
    public sealed class DPod : DynamicObject, INotifyPropertyChanged, IDPod
    {
        public event PropertyChangedEventHandler PropertyChanged, PropertyColumn;

        private ConcurrentDictionary<string, BeanBase> _beans;
        private SortedDictionary<int, string> _names = new SortedDictionary<int, string>();
        private string _serviceKey { get; set; }
        private string _user { get; set; }

        //construct
        public DPod(IEnumerable<IBean> pod) : this(pod, null, null) { }

        public DPod(IEnumerable<IBean> pod, string serviceKey, string user) { Initialise(pod, _serviceKey = serviceKey, _user = user); }

        //override
        public override bool TryGetMember(GetMemberBinder binder, out object result)
        {
            BeanBase bean;
            if (_beans.TryGetValue(binder.Name, out bean))
            {
                result = bean.Object;
                return true;
            }
  
            result = string.Empty;
            return false;
        }

        public override bool TrySetMember(SetMemberBinder binder, object value)
        {
            BeanBase bean;
            if (!_beans.TryGetValue(binder.Name, out bean))
                return false;

            _beans[binder.Name].Object = value;
            _beans[binder.Name].TimeStamp = DateTime.Now;
            _beans[binder.Name].User = _user;
            
            NotifyPodChanged(binder.Name);

            return true;
        }

        public override IEnumerable<string> GetDynamicMemberNames()
        {
            if (_names != null)
                return _names.Values;
            return _beans.Keys;
        }

        //public
        public string ServiceKey() { return _serviceKey;}

        public ConcurrentDictionary<string, BeanBase>  GetBeans() { return _beans; }

        public IPod ToPod()
        {
            object id = -1;
            BeanBase bean;
            _beans.TryGetValue(BeanField.Id.ToString(), out bean);

            var pod = new Pod() { Id = int.Parse(bean.Object.ToString())};
            foreach (var b in _beans.Values)
                pod.Add(b.Name, b.Object, b.User, (int)id, b.Index, b.TimeStamp);

            return pod;
        }

        public void SetValue(string name, object value, string user, DateTime timeStamp, int index = -1)
        {
            BeanBase bean;
            if (!_beans.TryGetValue(name, out bean))
                _beans[name] = bean = new BeanBase() { Index = index };

            bean.Name = name;
            bean.Object = value;
            bean.TimeStamp = timeStamp;
            bean.User = user;

            NotifyPodChanged(name);
        }

        public void SetBean(IBean bean)
        {
            SetValue(bean.Name, bean.Type.Equals("Pod") ? bean.ToString() : bean.Object, bean.User, bean.TimeStamp, bean.Index);
            AddColumn(bean);
        }

        public IBean GetBean(string name)
        {
            BeanBase bean;
            if (_beans.TryGetValue(name, out bean))
                return new Bean(bean);
            return null;
        }

        public object GetValue(string name)
        {
            BeanBase bean;
            if (_beans.TryGetValue(name, out bean))
                return bean.Object;
            return string.Empty;
        }

        public DateTime GetTimeStamp(string name)
        {
            return _beans.ContainsKey(name) ? _beans[name].TimeStamp : DateTime.MinValue;
        }

        public string GetType(string name)
        {
            BeanBase bean;
            if (_beans.TryGetValue(name, out bean))
                return bean.Object.GetType().Name;
            return null;
        }

        public bool TryGetObject(string name, out object value)
        {
            BeanBase bean;
            if (_beans.TryGetValue(name, out bean))
            {
                value = bean.Object;
                return true;
            }
            value = string.Empty;
            return false;
        }

        public bool HasObject(string name, object value)
        {
            BeanBase bean;
            if (!_beans.TryGetValue(name, out bean))
                return false;
                
            if(bean.Object.ToString().Split(',').Contains(value.ToString()))
                return true;
            
            return false;
        }

        public new string ToString()
        {
            var s = new StringBuilder();
            foreach (var kvp in _beans)
            {
                s.Append(kvp.Key.ToString());
                s.Append(',');
            }
            s.Append(';');
            foreach (var kvp in _beans)
            {
                s.Append(kvp.Value.Object.ToString());
                s.Append(',');
            }
            return s.ToString();
        }

        //private
        private void Initialise(IEnumerable<IBean> pod, string serviceKey, string user)
        {
            _beans = new ConcurrentDictionary<string, BeanBase>();
            foreach (var bean in pod)
                SetBean(bean);
        }

        private void AddColumn(IBean bean)
        {
            if (!_names.ContainsValue(bean.Name))
            {
                _names[bean.Index] = bean.Name;
                NotifyPropertyColumn(bean.Name);
            }
        }
        
        private void NotifyPodChanged(string name)
        {
            if (PropertyChanged != null)
                PropertyChanged.Invoke(this, new PropertyChangedEventArgs(name));
        }

        private void NotifyPropertyColumn(string name)
        {
            if (PropertyColumn != null)
                PropertyColumn.Invoke(this, new PropertyChangedEventArgs(name));
        }

        //event
        private void Bean_Updated(object s, BeanEventArgs a)
        {
            a.Bean.User = _user;
            SetBean(a.Bean);
        }
    }
}
